package com.wpre.lotterysystem.service.Impl;

import com.wpre.lotterysystem.common.errcode.ServiceErrorCodeConstants;
import com.wpre.lotterysystem.common.exception.ServiceException;
import com.wpre.lotterysystem.common.utils.JacksonUtil;
import com.wpre.lotterysystem.common.utils.RedisUtil;
import com.wpre.lotterysystem.controller.param.CreateActivityParam;
import com.wpre.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.wpre.lotterysystem.controller.param.CreateUserByActivityParam;
import com.wpre.lotterysystem.controller.param.PageParam;
import com.wpre.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.wpre.lotterysystem.dao.dataobject.ActivityUserDO;
import com.wpre.lotterysystem.dao.dataobject.PrizeDO;
import com.wpre.lotterysystem.dao.mapper.*;
import com.wpre.lotterysystem.dao.dataobject.ActivityDO;
import com.wpre.lotterysystem.service.ActivityService;
import com.wpre.lotterysystem.service.dto.ActivityDTO;
import com.wpre.lotterysystem.service.dto.ActivityDetailDTO;
import com.wpre.lotterysystem.service.dto.CreateActivityDTO;
import com.wpre.lotterysystem.service.dto.PageListDTO;
import com.wpre.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityStatusEnum;
import com.wpre.lotterysystem.service.enums.ActivityTiersEnum;
import com.wpre.lotterysystem.service.enums.ActivityUserStatusEnum;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LianBao
 * Date: 2024-12-21
 * Time: 16:27
 */
@Service

public class ActivityServiceImpl implements ActivityService {

    private final String ACTIVITY_PREFIX = "ACTIVITY";//活动缓存前缀(为了区分业务)
    private final Long ACTIVITY_TIME_OUT = 60 * 60 * 24 * 3L;//缓存3天


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 创建活动
     *
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)   //如果出现异常了就回滚
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //1.校验活动信息是否正确
        checkActivityInfo(param);

        //2.保存活动信息到表里
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //3.保存活动奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        //批量插入奖品信息
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //4.保存人员信息
        List<CreateUserByActivityParam> activityUserList = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = activityUserList.stream()
                .map(userParam -> {
                    ActivityUserDO userDO = new ActivityUserDO();
                    userDO.setActivityId(activityDO.getId());
                    userDO.setUserId(userParam.getUserId());
                    userDO.setUserName(userParam.getUserName());
                    userDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return userDO;
                })
                .collect(Collectors.toList());


        activityUserMapper.batchInsert(activityUserDOList);
        //5.整合完整的活动信息
        //获取需要查询的奖品id
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        //先获取奖品基本属性
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);


        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList, activityPrizeDOList, prizeDOList);

        //6.将活动信息存放到Redis中
        cacheActivity(detailDTO);

        //7.构造返回
        CreateActivityDTO result = new CreateActivityDTO();
        result.setActivityId(activityDO.getId());
        return result;
    }

    /**
     * 翻页查询活动列表
     *
     * @param param
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //1.获取总量
        int total = activityMapper.count();

        //2.获取当前页列表
        List<ActivityDO> list = activityMapper.selectActivityList(param.offset(), param.getPageSize());

        List<ActivityDTO> activityDTOList = list
                .stream()
                .map(activityDO -> {
                    ActivityDTO dto = new ActivityDTO();
                    dto.setActivityId(activityDO.getId());
                    dto.setActivityName(activityDO.getActivityName());
                    dto.setDescription(activityDO.getDescription());
                    dto.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return dto;
                }).collect(Collectors.toList());

        return new PageListDTO<>(total, activityDTOList);
    }

    //获取活动详细信息
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if (activityId == null) {

            return null;
        }
        //1.查Redis
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if (activityDetailDTO != null) {
            //缓存中存在,直接返回
            return activityDetailDTO;
        }

        //2.Redis不存在,查表:
        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectPrizeById(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectUserById(activityId);
        //奖品表
        //先获取到奖品的id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());

        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合详细信息存放到Redis中
        activityDetailDTO = convertToActivityDetailDTO(aDO, auDOList, apDOList, pDOList);
        cacheActivity(activityDetailDTO);

        //3.返回
        return activityDetailDTO;
    }

    //更新缓存
    @Override
    public void cacheUpdateActivity(Long activityId) {
        if (activityId == null) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_CACHE_IS_EMPTY);
        }

        //2.Redis不存在,查表:
        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);
        if (aDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_CACHE_IS_EMPTY);
        }
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectPrizeById(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectUserById(activityId);
        //奖品表
        //先获取到奖品的id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());

        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合详细信息存放到Redis中
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(aDO, auDOList, apDOList, pDOList);
        cacheActivity(detailDTO);
        //整合完整的活动信息
    }

    /**
     * 缓存完整的活动信息
     *
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //key:ACTIVITY_12
        //value:detailDTO(先转换成json)
        if (detailDTO == null || detailDTO.getActivityId() == null) {
            return;
        }

        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIME_OUT);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
    根据活动id从缓存中获取详细信息
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (activityId == null) {
            return null;
        }
        try {
            String s = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(s)) {
                return null;
            }
            return JacksonUtil.readValue(s, ActivityDetailDTO.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 整合完整的活动信息
     *
     * @param activityDO
     * @param activityUserDOList
     * @param activityPrizeDOList
     * @param prizeDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<PrizeDO> prizeDOList) {

        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        List<ActivityDetailDTO.PrizeDetailDTO> prizeDetailDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDetailDTO prizeDetailDTO = new ActivityDetailDTO.PrizeDetailDTO();
                    prizeDetailDTO.setPrizeId(apDO.getPrizeId());

                    Optional<PrizeDO> optionalActivityPrizeDO = prizeDOList.stream().
                            filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    optionalActivityPrizeDO.ifPresent(prizeDO -> {
                        prizeDetailDTO.setName(prizeDO.getName());
                        prizeDetailDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDetailDTO.setPrice(prizeDO.getPrice());
                        prizeDetailDTO.setDescription(prizeDO.getDescription());
                    });

                    prizeDetailDTO.setTiers(ActivityTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDetailDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDetailDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDetailDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDetailDTOList);

        List<ActivityDetailDTO.UserDetailDTO> userDetailDTOList = activityUserDOList.stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDetailDTO userDetailDTO = new ActivityDetailDTO.UserDetailDTO();
                    userDetailDTO.setUserId(auDO.getUserId());
                    userDetailDTO.setUserName(auDO.getUserName());
                    userDetailDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));

                    return userDetailDTO;
                }).collect(Collectors.toList());

        detailDTO.setUserDetailDTOList(userDetailDTOList);

        return detailDTO;
    }

    /**
     * 校验活动有效性
     *
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY_ERROR);
        }
        //1.校验人员id是否存在
        //将参数中的所有的id转换成一个list
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)  //映射id
                .distinct()//id去重
                .collect(Collectors.toList());

        //从数据库中查询所有的id
        List<Long> existIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }

        //校验参数中所有的id是否在数据库中存在
        userIds.forEach(id -> {
            if (!existIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //2.校验奖品id是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct() //id去重
                .collect(Collectors.toList());
        //
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //3.人员数量大于等于奖品数量
        long userCount = param.getActivityUserList().size();
        long prizeCount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userCount < prizeCount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //4.校验活动奖品等级的有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });


    }
}
